Skip to content

feat(primitives): add Rx compatibility APIs and advanced sinks#63

Merged
glennawatson merged 18 commits into
mainfrom
CP_dynamicdata-compat-operators
Jun 21, 2026
Merged

feat(primitives): add Rx compatibility APIs and advanced sinks#63
glennawatson merged 18 commits into
mainfrom
CP_dynamicdata-compat-operators

Conversation

@ChrisPulman

@ChrisPulman ChrisPulman commented Jun 20, 2026

Copy link
Copy Markdown
Member

What kind of change does this PR introduce?

  • Feature/API compatibility work for Rx and DynamicData-style migration scenarios.
  • Public Advanced API exposure for allocation-sensitive operator construction.

What is the new behavior?

  • Adds Rx-compatible operator names and aliases across ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive:
    • Publish, Replay, RefCount, SubscribeSafe, StartWith, Finally, Throttle, Catch, timed Buffer, SelectMany, and bounded enumerable Merge/Blend.
  • Adds observable/signal factory compatibility APIs used by Rx-first source bases and DynamicData-style call sites.
  • Hardens connectable signal lifetime behavior so connection-owned disposables remain tied to the active connection.
  • Exposes generally useful operator implementation types in the Advanced namespace so performance-sensitive consumers can construct signals, sinks, and witnesses directly:
    • MapIndexedSignal and MapIndexedWitness.
    • MaxConcurrentBlendCoordinator and MaxConcurrentEnumerableBlendSignal.
    • SelectManyEnumerableObserver and SelectManyEnumerableSignal.
    • SubscribeSafeObserver.
    • SynchronizeObjectSignal and SynchronizeObjectWitness.
    • TaskChainCoordinator and TaskChainSignal.
  • Updates migration guidance, tests, and PublicAPI baselines for ReactiveUI.Primitives, ReactiveUI.Primitives.Reactive, and ReactiveUI.Primitives.Core.

What is the current behavior?

  • Consumers migrating from System.Reactive or DynamicData-heavy code still need project-local compatibility helpers for common Rx names.
  • Some reusable operator implementation types are private nested classes, so consumers cannot opt into direct construction to avoid method/delegate allocation overhead.
  • PublicAPI baselines do not yet include the compatibility APIs and Advanced implementation types added by this branch.

What might this PR break?

  • Public API surface increases across ReactiveUI.Primitives, ReactiveUI.Primitives.Reactive, and ReactiveUI.Primitives.Core.
  • No intended source-breaking or behavior-breaking changes for existing consumers.

Checklist

  • I have read the Contribute guide
  • Tests have been added or updated (for bug fixes / features)
  • Docs have been added or updated (for bug fixes / features)
  • Changes target the main branch
  • PR title follows Conventional Commits

Additional information

  • Local validation completed:
    • dotnet build "ReactiveUI.Primitives.slnx" -c Release -v:minimal
    • dotnet test "ReactiveUI.Primitives.slnx" -c Release --no-build
  • Earlier targeted validation covered:
    • ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive net8.0 builds.
    • ReactiveUI.Primitives.Tests net8.0/net10.0 builds and net10.0 executable run.
    • PublicAPI generation for ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive.

API surface:
- Add Rx-compatible Signal aliases for Return, Empty, Never, Throw, Timer, Merge, and Switch.
- Add async Create/Defer overloads, indexed Select/MapIndexed, Task ToObservable/ToSignal, task Concat/Chain, scheduler-aware ToObservable, and object Synchronize/SynchronizeObject support.
- Update public API baselines for ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive.

Build and test fixes:
- Fix generate-publicapi.ps1 path construction under Windows PowerShell.
- Disambiguate tests after the new overloads.
- Disable the WindowsAppSDK Undocked RegFree WinRT auto-initializer for WinUI tests to avoid compiling package source that violates CA5392 while preserving self-contained dispatcher tests.
Comment thread src/Primitives.Shared/SignalOperatorMixins.StatefulSignals.cs Fixed
@ChrisPulman ChrisPulman changed the title feat: add DynamicData observable compatibility APIs feat: add Observable compatibility APIs Jun 20, 2026
Operators:

- add Rx-name aliases for Publish, Replay, RefCount, SubscribeSafe, StartWith, Buffer, Finally, Throttle, Catch, SelectMany, and Merge

- add bounded enumerable Blend/Merge support so migrated DynamicData pipelines can avoid local ReactiveCompatibility operators

- add cleanup argument validation for Finally/OnCleanup parity

Public API:

- regenerate ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive shipped API baselines for all target frameworks

Tests:

- add TUnit coverage for DynamicData migration aliases, SubscribeSafe failure handling, bounded Merge, Buffer, Throttle, and Finally

Validation:

- built ReactiveUI.Primitives, ReactiveUI.Primitives.Reactive, DynamicData, DynamicData.Reactive, and the solution

- ran ReactiveUI.Primitives.Tests net10.0 executable: 391/391 passed
@ChrisPulman ChrisPulman changed the title feat: add Observable compatibility APIs feat: add DynamicData Rx compatibility operators Jun 20, 2026
Update StyleSharp.Analyzers package version from 3.11.1 to 3.11.2 in src/Directory.Packages.props to pick up the patch release.
Comment thread src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs Fixed
Comment thread src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs Fixed
Comment thread src/Primitives.Shared/ConnectableSignalExtensions.cs Fixed
Comment thread src/Primitives.Shared/LinqExtensions.BlendEnumerable.cs Fixed
Comment thread src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs Fixed
Comment thread src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs Fixed
@ChrisPulman ChrisPulman changed the title feat: add DynamicData Rx compatibility operators feat: add Rx compatibility operators Jun 20, 2026
Review comments:

- address generic catch review threads by filtering fatal runtime exceptions before converting ordinary delegate/enumerator failures to OnError

Implementation:

- add a shared FatalExceptionHelper used by Connectable Publish, bounded Blend/Merge, MapIndexed, SubscribeSafe, and enumerable SelectMany

Validation:

- dotnet build ReactiveUI.Primitives net8.0

- dotnet build ReactiveUI.Primitives.Reactive net8.0

- dotnet build ReactiveUI.Primitives.Tests net10.0

- ReactiveUI.Primitives.Tests net10.0 executable: 391/391 passed
CI:

- add Android-generated Resource type baselines for ReactiveUI.Primitives and ReactiveUI.Primitives.Reactive on net10.0-android and net11.0-android

Validation:

- dotnet build ReactiveUI.Primitives net10.0-android Release

- dotnet build ReactiveUI.Primitives.Reactive net10.0-android Release

- net11.0-android local validation is blocked by missing Android API level 37 on this machine; CI has the required SDK
@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.85%. Comparing base (cda2956) to head (f318f5b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #63      +/-   ##
==========================================
+ Coverage   93.36%   93.85%   +0.49%     
==========================================
  Files         462      480      +18     
  Lines       17208    17736     +528     
  Branches     2052     2101      +49     
==========================================
+ Hits        16067    16647     +580     
+ Misses        898      856      -42     
+ Partials      243      233      -10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Review coverage:

- add focused TUnit coverage for Rx factory aliases, connectable aliases, SubscribeSafe, bounded Merge, SelectMany enumerable branches, event-pattern delegate variants, and Optional null semantics

- raise local patch coverage for coverable runtime additions to 98.54%

Runtime fixes:

- keep async Signal.Create subscription cancellation lifetime valid after async setup completes

- move Signal<T> dispatch helper into the static helper section to satisfy SST1204 without suppression

API and build support:

- ship Range, Interval, and Concat Rx alias API baselines for Primitives and Primitives.Reactive

- add netfx AllowNullAttribute polyfill support for the updated Optional API
## Reactive operators

- Preserve Rx SelectMany concurrent flattening semantics by delegating through Map(...).Merge().

- Add coverage for concurrent inner emissions used by DynamicData-style Rx migration code.

## Connectable signals

- Replace disposable-field suppressions with an actual IDisposable implementation on ConnectableSignal<T>.

- Latch terminal/disposed state to prevent reconnecting completed or disposed connectable sources.

- Add public API baseline entries and focused TUnit coverage for disposal and RefCount reconnection behavior.

## Test stability

- Make the async throttle supersession test deterministic with a manual TimeProvider instead of wall-clock timing.
## What changed

- Increase the default timeout used by string-pattern Filter regex construction from one second to thirty seconds.

- Preserve explicit Regex overload behavior so caller-supplied timeout settings and error forwarding are unchanged.

## Validation

- Built ReactiveUI.Primitives.Extensions.Tests for net10.0 and net9.0 Release.

- Ran the net10.0 and net9.0 Release Microsoft Testing Platform executables.

- Ran net10.0 Release MTP coverage for extension tests and inspected the changed shared extension file with the MTP coverage MCP.

- Built ReactiveUI.Primitives.slnx locally.
Remove the accidental public ConnectableSignal<T>.Dispose() API from the DynamicData compatibility branch while preserving the terminal latch used by Publish().RefCount().

Update Core public API baselines and remove the test that asserted the rejected public dispose surface. The connection lifetime remains owned by Connect() callers.

Validated with ReactiveUI.Primitives.Tests net8.0, ReactiveUI.Primitives.Reactive net8.0, and the TUnit test executable.
## What changed

- Remove the CA1001/S2931 suppression attributes from ConnectableSignal<T>.

- Keep Connect() caller-owned lifetime semantics by storing the active connection in a non-disposable StrongBox slot while the returned Connection handle owns disposal.

- Preserve the terminal latch behavior added for Publish().RefCount().

## Validation

- Built ReactiveUI.Primitives.Core for net10.0.

- Built and ran ReactiveUI.Primitives.Tests net10.0 with Microsoft Testing Platform/TUnit.

- Built ReactiveUI.Primitives.slnx locally with Android and Apple target framework exclusions.
GitHub only registered the standalone CodeQL check for the previous PR head. This no-op commit forces a fresh pull_request synchronize event so Build, SonarCloud, and CodeQL all attach to the current branch head.
## What changed

- Update README package guidance for the current ReactiveUI.Primitives package set, including reactive UI package variants.

- Document multi-source SyncLatest/CombineLatest, Rx-name SelectMany compatibility, and Filter regex timeout behavior.

- Expand README migration guidance for an existing lean xyz project and a new xyz.Reactive package using ReactiveUI.Primitives.Reactive packages.

- Update packaged Skill.md frontmatter, package chooser, API landmarks, framework notes, and Rx migration playbook.

## Validation

- Ran git diff --check.

- Packed ReactiveUI.Primitives with dotnet pack --no-build and verified README.md, Skill.md, and .agents/skills/reactiveui-primitives/SKILL.md are present in the nupkg.
@ChrisPulman ChrisPulman marked this pull request as ready for review June 20, 2026 19:21
ChrisPulman and others added 2 commits June 20, 2026 23:21
Replace the task Chain/Concat implementation that composed ChainSignal over sources.Map(Signal.FromTask) with a dedicated TaskChainSignal/TaskChainCoordinator path.

This removes the extra extension-method hop called out in PR review while preserving sequential task result ordering, source errors, task faults, null-task failures, and disposal behavior.

Validation: dotnet build tests/ReactiveUI.Primitives.Tests -c Debug -f net10.0; ReactiveUI.Primitives.Tests.exe --results-directory TestResultsScratch/TaskChainDirect2 --output Normal; ReactiveUI.Primitives.Tests.exe --results-directory TestResultsScratch/TaskChainDirectCoverage2 --output Normal --coverage; dotnet build ReactiveUI.Primitives.slnx -c Debug -p:AndroidPrimitivesTargetFrameworks= -p:ApplePrimitivesTargetFrameworks=
- Move branch-added operator signals and sinks out of private nested types.

- Add public API baselines for primitive and reactive Advanced namespaces.
@glennawatson glennawatson changed the title feat: add Rx compatibility operators feat(primitives): add Rx compatibility APIs and advanced sinks Jun 20, 2026
- Move the async create subscription lifetime helper into Advanced.

- Add public API baselines for primitive and reactive packages.
- Add focused tests for async lifetimes, connectable signals, create overloads, fatal exceptions, and advanced coordinators.

- Extract small internal helpers for shared observer lifetimes and hard-to-reach coordinator guards.
Comment thread src/Primitives.Shared/Advanced/ObserverSinkLifetime.cs
Comment thread src/Primitives.Shared/Advanced/ObserverSinkLifetime.cs
glennawatson and others added 2 commits June 21, 2026 10:45
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

@glennawatson glennawatson merged commit 346adcb into main Jun 21, 2026
13 checks passed
@glennawatson glennawatson deleted the CP_dynamicdata-compat-operators branch June 21, 2026 01:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants